#include "monitorsstf.h"

//Constructor
monitor::monitor(logger *log) {
	this->log = log;
	pthread_mutex_init(&lock, NULL);
	pthread_mutex_init(&completeLock, NULL);
	pthread_cond_init(&inputCheck, NULL);
	liveClients = 0;
	newReqFlag = false;
	reqMade = 0;
	reqProc = 0;
	lastTrack = 0;
}

//Destructor
monitor::~monitor() {
	buffer[0] = 0;
	pthread_mutex_destroy(&lock);
	pthread_mutex_destroy(&completeLock);
	pthread_cond_destroy(&inputCheck);
	//Destroy inputQ and outputQ when theyre DS have been declared	
}

//Add request. Takes a single request and adds it to input DS. 
bool monitorsstf::addRequest(request* req) {
	pthread_mutex_lock(&lock);
	
	bool ret_val = false;
	if (!newReqFlag) {
		newReqFlag = true;
		inputQ.push(req);
		ret_val = true;
		reqMade ++;
		// Debug statement
		sprintf(buffer, "Request added. Client %d\nDisk track is %d, Disk work is %d", req->getRequestingTid(), req->getTrackNum(), req->getWorkSize());
		log->debug(buffer, "c");
		buffer[0] = 0;
		pthread_cond_wait(&(req->CVWorkComplete), &lock);
		// Debug statement
		sprintf(buffer, "...Client %d finished waiting. Request finished.",req->getRequestingTid());
		log->debug(buffer, "c");
		buffer[0] = 0;
	}
	pthread_mutex_unlock(&lock);
	return ret_val;
}

//Schedules all requests in input DS by moving them to output DS. 
bool monitorsstf::scheduleRequests() {
		queue<request*> sortQ;
        pthread_mutex_lock(&lock);  
        bool ret_val = false;
        request* cur_req;
        request* min_req;
        int track_comp;
        
        if (newReqFlag) {
                if (inputQ.empty()) {
						log->debug("Input queue is empty.", "c");
                } else {                           // The sorting algorithm begins here
                		int ref_track = lastTrack;               		
                		// Place outputQ entries and inputQ entries into a single sorting queue
                        while(!outputQ.empty()){
                        	cur_req = outputQ.front();
                        	outputQ.pop();
                        	sortQ.push(cur_req);      	
                        }
                        while(!inputQ.empty()){
                        	cur_req = inputQ.front();
                        	inputQ.pop();
                        	sortQ.push(cur_req); 
                        }
                        while(!sortQ.empty()){
                        	min_req = sortQ.front();
                        	sortQ.pop();
                        	track_comp = min_req->getTrackNum();
                        	while (track_comp == ref_track){                    		
                        		outputQ.push(min_req);
                        		sprintf(buffer, "Pushed request %d, on track %d  (1st push point)", min_req->getRequestingTid(), min_req->getTrackNum());
								log->debug(buffer, "c");
                        		if(!sortQ.empty()){
                        			min_req = sortQ.front();
                        			sortQ.pop();
                        			track_comp = min_req->getTrackNum();
                        		}
                        		else{
                        			min_req = NULL;
                        			track_comp = 16;
                        		}                 		
                        	}                    	
                        	for(int i = 0; i < sortQ.size(); i++){                        		
                        		cur_req = sortQ.front();
                        		sortQ.pop();
                        		if(abs(cur_req->getTrackNum() - ref_track) < abs(min_req->getTrackNum() - ref_track)){
                        			/**if(cur_req->getTrackNum() == ref_track){              				
                        				i = 0;
                        				outputQ.push(cur_req);         
                        				cout << "Pushed request " << cur_req->getRequestingTid() << ", on track " << cur_req->getTrackNum() << " (line 2)" << endl;
                        				// if the current request is on the current disk track,
                        				// we can push it straight onto the outputQ; it must be a true min_req
                        			}**/
                        			//else{
                        				sortQ.push(min_req);
                        				min_req = cur_req;
                        			//}
                        		}
                        		else{
                        			sortQ.push(cur_req);
                        		}
                        	}
                        	if (track_comp != 16){
                        		outputQ.push(min_req);  
                        		sprintf(buffer, "Pushed request %d, on track %d  (2nd push point)", min_req->getRequestingTid(), min_req->getTrackNum());
								log->debug(buffer, "c");  	                      	
                        		ref_track = min_req->getTrackNum(); 
                        	}     
                        	          
                        }
                        ret_val = true;
                        newReqFlag = false;
						log->debug("Pending requests are as follows:", "c");
                        for (int i = 0; i < outputQ.size(); i++){
                        	cur_req = outputQ.front();
                        	outputQ.pop();
                        	sprintf(buffer, "%d. tid = %d, track no. = %d", i + 1, cur_req->getRequestingTid(), cur_req->getTrackNum());
							log->debug(buffer, "c"); 
                        	outputQ.push(cur_req);
                        }
                        pthread_cond_signal(&inputCheck);
                }
        }

        pthread_mutex_unlock(&lock);
        return ret_val;
}

//Process request. Removes a single request from the output DS. 
request* monitorsstf::removeRequest() {
	//if FLAG condition variable wait until signaled
	pthread_mutex_lock(&lock);
	if (newReqFlag) {
		pthread_cond_wait(&inputCheck, &lock);
	}
	request* temp;
	if (!outputQ.empty()) {
		temp = outputQ.front();
		outputQ.pop();
		reqProc ++;
		lastTrack = temp->getTrackNum();
		log->debug("Request processed", "c"); 
	} else { temp = NULL;}
	pthread_mutex_unlock(&lock);
	return(temp);
}

/*
 *Once worker has processed the request and modified the request object
 *(code not needed to be protected), pass back the request object to the monitor
 * so we can signal the reqest objects condition_variable, and wake up the client
 */
void monitor::signalClient(request* req) {
	pthread_mutex_lock(&lock);
	pthread_cond_signal(&(req->CVWorkComplete));
	pthread_mutex_unlock(&lock);
}

/////////////////////////////////////////
// Methods for checking end conditions. 
/////////////////////////////////////////

bool monitorsstf::isWorkLeft() {
	pthread_mutex_lock(&lock);
	bool retVal = true;
	if (inputQ.empty() && outputQ.empty()) {
		retVal = false;
	}
	pthread_mutex_unlock(&lock);
	return retVal;
}

int monitor::getLiveClients() {
	//Note: When everything is working, we can get ride of the locks and see if it still works :P
	pthread_mutex_lock(&completeLock);
	int retVal = liveClients;
	pthread_mutex_unlock(&completeLock);
	return retVal;
}

void monitor::decLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients--;
	pthread_mutex_unlock(&completeLock);
}

void monitor::incLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients++;	
	pthread_mutex_unlock(&completeLock);
}

int monitor::getReqDiff() {
	return (reqMade - reqProc);
}

int monitor::getReqProc() {
	return reqProc;
}
// End guard
#endif
